package minidb.drive;

import java.util.List;
import minidb.index.bTreeIndex;
import minidb.record.*;
import minidb.transaction.transaction;
import minidb.type.*;
import static minidb.type.Const.*;

public class RecordOp
{
	private String tableName;
	public RecordOp(String tableName){
		this.tableName = tableName;	
	}
	
	public void InsertTuple(Tuple t) throws FileOpException 
	{
		TableInfo tab = tablePage.get(tableName);
		while(true){
			synchronized(tab.lock){
				if(tab.ID < 0 || Thread.currentThread().getId() == tab.ID)
					break;
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		RecordPage rec = new RecordPage(tab);
		bTreeIndex index = new bTreeIndex(tab, rec);
		List<fieldinfo> f = rec.fieldinfo();
		Record r = new Record(tab.recordlength(), rec.fieldinfo());
		int i;
		try{
			for(i = 0; i < f.size(); i++){
				String s = tableName + "." + f.get(i).name();
				Object o = t.GetFieldValue(s);
				if(o!=null)
					r.put(f.get(i).name(), toVal(o));
				else{
					if(f.get(i).type()==TYPE.INT)
						r.put(f.get(i).name(), new IntConstant());
					else if(f.get(i).type()==TYPE.CHAR || f.get(i).type()==TYPE.STRING)
						r.put(f.get(i).name(), new StringConstant());
					else if(f.get(i).type()==TYPE.DOUBLE)
						r.put(f.get(i).name(), new DoubleConstant());
				}
			}
		}catch(RuntimeException e){
			throw new FileOpException(e.getMessage());
		}
		RID rid = rec.Insert(r);
		try{
			index.Insert(r, rid);
			synchronized(tab.lock){
				if(tab.ID < 0)
					;
				else{
					transaction tx = TX.get(tab.ID);
					if(tab.ID == Thread.currentThread().getId() && !tx.isRollBack){
						int length = INT_SIZE*4+2+tableName.length();
						java.nio.ByteBuffer bt = java.nio.ByteBuffer.allocateDirect(length);
						bt.rewind();
						bt.putInt(length-INT_SIZE*2);
						bt.put((byte)tableName.length());
						bt.put(tableName.getBytes());
						bt.put((byte)1);
						bt.putInt(rid.pageNum());
						bt.putInt(rid.rid());
						bt.putInt(length-INT_SIZE*2);
						tx.add(bt, length);
					}
				}
			}
		}catch(Exception e){
			rec.delete(rid);
			index.close();
			throw new FileOpException(e.getMessage());
		}
		index.close();
	}
	
	public void UpdateTuple(String ridStr,Tuple new_t) throws FileOpException
	{
		TableInfo tab = tablePage.get(tableName);
		while(true){
			synchronized(tab.lock){
				if(tab.ID < 0 || Thread.currentThread().getId() == tab.ID)
					break;
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		RecordPage rec = new RecordPage(tab);
		bTreeIndex index = new bTreeIndex(tab, rec);
		RID rid = new RID(ridStr);
		List<fieldinfo> f = rec.fieldinfo();
		Record r = new Record(tab.recordlength(), rec.fieldinfo());
		int i;
		try{
			for(i = 0; i < f.size(); i++){
				String s = tableName + "." + f.get(i).name();
				Object o = new_t.GetFieldValue(s);
				if(o!=null)
					r.put(f.get(i).name(), toVal(o));
				else{
					if(f.get(i).type()==TYPE.INT)
						r.put(f.get(i).name(), new IntConstant());
					else if(f.get(i).type()==TYPE.CHAR || f.get(i).type()==TYPE.STRING)
						r.put(f.get(i).name(), new StringConstant());
					else if(f.get(i).type()==TYPE.DOUBLE)
						r.put(f.get(i).name(), new DoubleConstant());
				}
			}
		}catch(RuntimeException e){
			throw new FileOpException(e.getMessage());
		}
		r.rid(rid);
		Record old = rec.get(rid);
		old.rid(rid);
		try{
			index.Delete(old, rid);
			rec.update(r);
			index.Insert(r, rid);
			synchronized(tab.lock){
				transaction tx = TX.get(tab.ID);
				if(tab.ID == Thread.currentThread().getId() && !tx.isRollBack){
					int length = INT_SIZE*4+2+tableName.length()+tab.recordlength();
					java.nio.ByteBuffer bt = java.nio.ByteBuffer.allocateDirect(length);
					bt.rewind();
					bt.putInt(length-INT_SIZE*2);
					bt.put((byte)tableName.length());
					bt.put(tableName.getBytes());
					bt.put((byte)2);
					bt.putInt(rid.pageNum());
					bt.putInt(rid.rid());
					old.record().rewind();
					bt.put(old.record());
					bt.putInt(length-INT_SIZE*2);
					tx.add(bt, length);
				}
			}
			index.close();
		}catch(Exception e){
			rec.Rid(rid);
			rec.update(old);
			index.Insert(old, rid);
			index.close();
			throw new FileOpException(e.getMessage());
		}
	}
	
	public void DeleteTuple(String ridStr)
	{
		TableInfo tab = tablePage.get(tableName);
		while(true){
			synchronized(tab.lock){
				if(tab.ID < 0 || Thread.currentThread().getId() == tab.ID)
					break;
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		RecordPage rec = new RecordPage(tab);
		bTreeIndex index = new bTreeIndex(tab, rec);
		RID rid = new RID(ridStr);
		Record r = rec.get(rid);
		index.Delete(r, rid);
		rec.delete(rid);
		synchronized(tab.lock){
			transaction tx = TX.get(tab.ID);
			if(tab.ID == Thread.currentThread().getId() && !tx.isRollBack){
				int length = INT_SIZE*2+2+tableName.length()+tab.recordlength();
				java.nio.ByteBuffer bt = java.nio.ByteBuffer.allocateDirect(length);
				bt.rewind();
				bt.putInt(length-INT_SIZE*2);
				bt.put((byte)tableName.length());
				bt.put(tableName.getBytes());
				bt.put((byte)3);
				r.record().rewind();
				bt.put(r.record());
				bt.putInt(length-INT_SIZE*2);
				tx.add(bt, length);
			}
		}
		index.close();
	}
	
	private Constant toVal(Object o){
		if(Integer.class.isInstance(o))
			return new IntConstant((Integer)o);
		else if(String.class.isInstance(o))
			return new StringConstant((String)o);
		else if(Double.class.isInstance(o))
			return new DoubleConstant((Double)o);
		else
			return null;
	}
}
